package com._21cn.framework.utils.crypt;

import com._21cn.framework.utils.StringUtil;

import java.io.UnsupportedEncodingException;
import java.security.NoSuchAlgorithmException;
import java.security.Provider;
import java.security.SecureRandom;
import java.security.Security;
import java.security.spec.KeySpec;

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESKeySpec;
import javax.crypto.spec.DESedeKeySpec;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

/**
 * DES算法
 */
public class DES {
	
	public static final String DEFAULT_CHARSET = "utf-8";
	
	public static enum Algorithm{
		DES_ECB_PKCS5Padding,
		DES_ECB_PKCS7Padding,
		DESede_ECB_PKCS5Padding,
		DESede_ECB_PKCS7Padding,
		DES_CBC_PKCS5Padding,
		DES_CBC_PKCS7Padding,
		DESede_CBC_PKCS5Padding,
		DESede_CBC_PKCS7Padding;
		
		public String toString(){
			return name().replace( '_', '/' );
		}
	}
	
	
	/**
	 * 在运行时动态添加新安全算法
	 * @param p
	 */
	public static void loadProvider(Provider p){
		Security.addProvider(p);
	}

	/**
     * 生成DES算法密钥，算法为DES
     * @return DES算法密钥
     */
    @Deprecated
    public static byte[] generateKey() {
    	return generateKey("DES");
    }
    
    /**
     * 生成DES算法密钥
     * @return DES算法密钥
     */
    public static byte[] generateDESKey() {
    	return generateKey("DES");
    }

    /**
     * 生成DESede算法密钥
     * @return
     */
    public static byte[] generateDESedeKey() {
    	return generateKey("DESede");
    }

    public static byte[] generateKey( String algorithm ) {
        try {
            // 生成一个DES算法的KeyGenerator对象
            KeyGenerator kg = KeyGenerator.getInstance(algorithm);
            kg.init( new SecureRandom() );

            // 生成密钥
            SecretKey secretKey = kg.generateKey();
            // 获取密钥数据
            byte[] key = secretKey.getEncoded();
            return key;
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        return null;
    }
    
    @Deprecated
    public static SecretKey generateSecret( byte[] key ) throws Exception{
    	return getSecretKey( key, "DES" );
    }
    
    public static SecretKey getDESSecretKey( byte[] key ) throws Exception{
    	return getSecretKey( key, "DES" );
    }
    
    public static SecretKey getDESedeSecret( byte[] key ) throws Exception{
    	return getSecretKey( key, "DESede" );
    }
    
    public static SecretKey getSecretKey( byte[] key, String algorithm ) throws Exception{
        // 从原始密钥数据创建DESKeySpec对象
        KeySpec ks = null;
        if( "DESede".equalsIgnoreCase( algorithm ) && key.length == DESedeKeySpec.DES_EDE_KEY_LEN )
        	ks = new DESedeKeySpec( key );
        else if( "DES".equalsIgnoreCase( algorithm ) && key.length == DESKeySpec.DES_KEY_LEN )
        	ks = new DESKeySpec( key );
        else {
        	return null;
        }

        // 创建一个密匙工厂，然后用它把DESKeySpec转换成一个SecretKey对象
        SecretKeyFactory keyFactory = SecretKeyFactory.getInstance( algorithm );
        return keyFactory.generateSecret(ks);
        //return new SecretKeySpec(key, algorithm);
    }

    /**
     * 加密函数
     * @param plain    明文数据
     * @param key     密钥
     * @param iv     加密向量(CBC模式时需要)
     * @param algorithm 算法，如 DES/ECB/PKCS5Padding
     * @return 返回加密后的数据
     */
    public static byte[] encrypt( byte[] plain, byte[] key, byte[] iv, Algorithm algorithm ) {
    	return endecrypt( Cipher.ENCRYPT_MODE, plain, key, iv, algorithm.toString() );
    }

    /**
     * 加密函数
     * @param plain    明文数据
     * @param key     密钥
     * @param iv     加密向量(CBC模式时需要)
     * @param algorithm 算法，如 DES/ECB/PKCS5Padding
     * @return 返回加密后的数据
     */
    public static byte[] encrypt( byte[] plain, byte[] key, byte[] iv, String algorithm ) {
    	return endecrypt( Cipher.ENCRYPT_MODE, plain, key, iv, algorithm );
    }

    /**
     * 加密函数
     * @param plain    明文数据
     * @param hexKey     密钥
     * @param iv     加密向量(CBC模式时需要)
     * @param algorithm 算法，如 DES/ECB/PKCS5Padding
     * @return 返回加密后的数据(hexString)
     */
    public static String encrypt( String plain, String hexKey, String iv, Algorithm algorithm ) {
    	return encrypt( plain, hexKey, iv, algorithm.toString() );
    }
    
    public static String encrypt( String plain, String hexKey, String iv, String algorithm ) {
    	String result = "";
    	if( StringUtil.isEmpty( plain ) )
    		return result;
    	
    	try {
        	byte[] plainbytes = plain.getBytes( DEFAULT_CHARSET );
        	byte[] ivbytes = StringUtil.isEmpty( iv )? null: iv.getBytes( DEFAULT_CHARSET );
	        byte[] data = endecrypt( Cipher.ENCRYPT_MODE, plainbytes, StringUtil.hex2Bytes( hexKey ), ivbytes , algorithm );
	        result = StringUtil.toHex( data );
        }
        catch( UnsupportedEncodingException e ) {
	        e.printStackTrace();
        }
        return result;
    }
    
    /**
     * 加密函数
     * @param plain    明文数据
     * @param hexKey     密钥
     * @param algorithm 算法，如 DES/ECB/PKCS5Padding
     * @return 返回加密后的数据(hexString)
      */
    public static String encrypt( String plain, String hexKey, Algorithm algorithm ) {
    	return encrypt( plain, hexKey, null, algorithm );
    }
    
    /**
     * 加密函数
     * @param plain    明文数据
     * @param key     密钥
     * @param algorithm 算法，如 DES/ECB/PKCS5Padding
     * @return 返回加密后的数据
      */
    public static byte[] encrypt( byte[] plain, byte[] key, Algorithm algorithm ) {
    	return encrypt( plain, key, null, algorithm );
    }
 
    /**
     * 加密函数(算法DES/ECB/PKCS5Padding)
     * @param plain    明文数据
     * @param key     密钥
     * @return 返回加密后的数据
     */
    @Deprecated
    public static byte[] encrypt( byte[] plain, byte[] key ) {
    	return encrypt( plain, key, Algorithm.DES_ECB_PKCS5Padding );
    }

    /**
     * 解密函数
     * @param cliperBytes    密文数据
     * @param key     密钥
     * @param iv     加密向量(CBC模式时需要)
     * @param algorithm 算法，如 DES/ECB/PKCS5Padding
     * @return 返回加密后的数据
     */
    public static byte[] decrypt( byte[] cliperBytes, byte[] key, byte[] iv, Algorithm algorithm ) {
    	return endecrypt( Cipher.DECRYPT_MODE, cliperBytes, key, iv, algorithm.toString() );
    }

    /**
     * 解密函数
     * @param cliperBytes    密文数据
     * @param key     密钥
     * @param iv     加密向量(CBC模式时需要)
     * @param algorithm 算法，如 DES/ECB/PKCS5Padding
     * @return 返回加密后的数据
     */
    public static byte[] decrypt( byte[] cliperBytes, byte[] key, byte[] iv, String algorithm ) {
    	return endecrypt( Cipher.DECRYPT_MODE, cliperBytes, key, iv, algorithm );
    }

    /**
     * 解密函数
     * @param clipers    密文数据
     * @param hexKey     密钥
     * @param iv     加密向量(CBC模式时需要)
     * @param algorithm 算法，如 DES/ECB/PKCS5Padding
     * @return 返回加密后的数据
     */
    public static String decrypt( String clipers, String hexKey, String iv, Algorithm algorithm ) {
    	return decrypt( clipers, hexKey, iv, algorithm.toString() );
    }
   
    /**
     * 解密函数
     * @param clipers    密文数据
     * @param hexKey     密钥
     * @param iv     加密向量(CBC模式时需要)
     * @param algorithm 算法，如 DES/ECB/PKCS5Padding
     * @return 返回加密后的数据
     */
    public static String decrypt( String clipers, String hexKey, String iv, String algorithm ) {
    	String result = "";
    	if( StringUtil.isEmpty( clipers ) )
    		return result;
    	
    	try {
        	byte[] cliperBytes = StringUtil.hex2Bytes( clipers );
        	byte[] ivbytes = StringUtil.isEmpty( iv )? null: iv.getBytes( DEFAULT_CHARSET );
	        byte[] data = endecrypt( Cipher.DECRYPT_MODE, cliperBytes, StringUtil.hex2Bytes( hexKey ), ivbytes , algorithm );
	        result = new String( data, DEFAULT_CHARSET );
        }
        catch( UnsupportedEncodingException e ) {
	        e.printStackTrace();
        }
        return result;
    }    
    
    /**
     * 解密函数(ECB模式)
     * @param clipers    密文数据
     * @param hexKey     密钥
     * @param algorithm 算法，如 DES/ECB/PKCS5Padding
     * @return 返回加密后的数据
     */
    public static String decrypt( String clipers, String hexKey, Algorithm algorithm ) {
    	return decrypt( clipers, hexKey, null, algorithm );
    }

    /**
     * 解密函数(ECB模式)
     * @param cliperBytes    密文数据
     * @param key     密钥
     * @param algorithm 算法，如 DES/ECB/PKCS5Padding
     * @return 返回加密后的数据
      */
    public static byte[] decrypt( byte[] cliperBytes, byte[] key, Algorithm algorithm ) {
    	return decrypt( cliperBytes, key, null, algorithm );
    }
 
    /**
     * 解密函数(算法DES/ECB/PKCS5Padding)
     * @param cliperBytes    密文数据
     * @param key     密钥
     * @return 返回加密后的数据
     */
    @Deprecated
    public static byte[] decrypt( byte[] cliperBytes, byte[] key ) {
    	return decrypt( cliperBytes, key, Algorithm.DES_ECB_PKCS5Padding );
    }
    
    /**
     * 加密函数(算法DES/CBC/PKCS5Padding)
     * @param data    明文数据
     * @param key     密钥
     * @return 返回加密后的数据
     */
    @Deprecated
    public static byte[] CBCEncrypt(byte[] data, byte[] key, byte[] iv) {
    	return encrypt( data, key, iv, Algorithm.DES_CBC_PKCS5Padding );
    }

    /**
     * 解密函数(算法DES/CBC/PKCS5Padding)
     * @param cliperBytes    密文数据
     * @param key     密钥
     * @return 返回加密后的数据
     */
    @Deprecated
    public static byte[] CBCDecrypt(byte[] cliperBytes, byte[] key, byte[] iv) {
    	return decrypt( cliperBytes, key, iv, Algorithm.DES_CBC_PKCS5Padding );
    }

    /**
     * 加密或解密函数
     * @param mode    工作方式
     * @param data    明文或已加密数据
     * @param key     密钥
     * @param iv     加密向量(CBC模式时需要)
     * @param algorithm 算法，如 DES/ECB/PKCS5Padding
     * @return 返回加密或解密后的数据
     */
    private static byte[] endecrypt( int mode, byte[] data, byte[] key, byte[] iv, String algorithm ) {
        try {
            SecretKey secretKey = new SecretKeySpec(key, algorithm.split( "/" )[0]);
            Cipher cipher = Cipher.getInstance( algorithm );
        	// 用密匙初始化Cipher对象
            if( iv != null && iv.length > 0 ){
                IvParameterSpec param = new IvParameterSpec(iv);
                cipher.init( mode, secretKey, param, new SecureRandom() );
            }
            else{
            	cipher.init( mode, secretKey, new SecureRandom() );
            }
            	
            byte encryptedData[] = cipher.doFinal(data);
            return encryptedData;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    public static void main(String[] args) {
        try {
        	String deskey = StringUtil.toHex( DES.generateDESKey() );
        	String desedekey = StringUtil.toHex( DES.generateDESedeKey() );
        	System.out.println( "deskey:" + deskey );
        	System.out.println( "desedekey:" + desedekey );
        	
        	String iv  = "12345678" ;
            String plain = "hello test";
            String encrypted = "";
            String decrypted = "";
            Algorithm algorithm = Algorithm.DES_CBC_PKCS5Padding;
            
            encrypted = DES.encrypt( plain, deskey, iv, algorithm );
            decrypted = DES.decrypt( encrypted , deskey, iv, algorithm );
            System.out.println(algorithm.toString()+ " encrypted:" + encrypted+" ,decrypted:" + decrypted );
            
            algorithm = Algorithm.DESede_CBC_PKCS5Padding;
            encrypted = DES.encrypt( plain, desedekey, iv, algorithm );
            decrypted = DES.decrypt( encrypted , desedekey, iv, algorithm );
            System.out.println(algorithm.toString()+ " encrypted:" + encrypted+" ,decrypted:" + decrypted );

            algorithm = Algorithm.DES_ECB_PKCS5Padding;
            encrypted = DES.encrypt( plain, deskey, algorithm );
            decrypted = DES.decrypt( encrypted , deskey, algorithm );
            System.out.println(algorithm.toString()+ " encrypted:" + encrypted+" ,decrypted:" + decrypted );

            algorithm = Algorithm.DESede_ECB_PKCS5Padding;
            encrypted = DES.encrypt( plain, desedekey, algorithm );
            decrypted = DES.decrypt( encrypted , desedekey, algorithm );
            System.out.println(algorithm.toString()+ " encrypted:" + encrypted+" ,decrypted:" + decrypted );
             
            
/*PKCS7Padding ：
1. 需要将类库bcprov-jdk1x-xxx.jar到jre/lib/ext目录中
2. 编辑并保存文件 jre/lib/security/java.security
根据JDK版本不同文件中可能包含不同数量的providers，在providers后增加
security.provider.N=org.bouncycastle.jce.provider.BouncyCastleProvider
N 为最后一个providers的下一个编号，例如：
security.provider.9=org.bouncycastle.jce.provider.BouncyCastleProvider
 */
            //DES.loadProvider( org.bouncycastle.jce.provider.BouncyCastleProvider.class.newInstance() );
            /*
            algorithm = Algorithm.DES_CBC_PKCS7Padding;
            encrypted = DES.encrypt( plain, deskey, iv, algorithm );
            decrypted = DES.decrypt( encrypted , deskey, iv, algorithm );
            System.out.println(algorithm.toString()+ " encrypted:" + encrypted+" ,decrypted:" + decrypted );
            
            algorithm = Algorithm.DESede_CBC_PKCS7Padding;
            encrypted = DES.encrypt( plain, desedekey, iv, algorithm );
            decrypted = DES.decrypt( encrypted , desedekey, iv, algorithm );
            System.out.println(algorithm.toString()+ " encrypted:" + encrypted+" ,decrypted:" + decrypted );

            algorithm = Algorithm.DES_ECB_PKCS7Padding;
            encrypted = DES.encrypt( plain, deskey, algorithm );
            decrypted = DES.decrypt( encrypted , deskey, algorithm );
            System.out.println(algorithm.toString()+ " encrypted:" + encrypted+" ,decrypted:" + decrypted );

            algorithm = Algorithm.DESede_ECB_PKCS7Padding;
            encrypted = DES.encrypt( plain, desedekey, algorithm );
            decrypted = DES.decrypt( encrypted , desedekey, algorithm );
            System.out.println(algorithm.toString()+ " encrypted:" + encrypted+" ,decrypted:" + decrypted );
            */
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    
    
}
